----------------------------------------------------------------------------------
-- Company: 
-- Engineer: 
-- 
-- Create Date:    14:07:09 12/28/2009 
-- Design Name: 
-- Module Name:    ALU - Behavioral 
-- Project Name: 
-- Target Devices: 
-- Tool versions: 
-- Description: 
--
-- Dependencies: 
--
-- Revision: 
-- Revision 0.01 - File Created
-- Additional Comments: 
--
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

-- General package
use work.GeneralProperties.ALL;

---- Uncomment the following library declaration if instantiating
---- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

entity ALU is
    Port ( A            : in  STD_LOGIC_VECTOR ((bus_size - 1) downto 0);
           B            : in  STD_LOGIC_VECTOR ((bus_size - 1) downto 0);
			  CarryIn      : in std_logic;
           AluSel       : in  ALU_operations;
			  ShifterSel   : in Shifter_operations;
           Output       : out STD_LOGIC_VECTOR ((bus_size - 1) downto 0);
			  FlagCarry    : out std_logic;
			  FlagZero     : out std_logic;
			  FlagSign     : out std_logic;
			  FlagOverflow : out std_logic
			  );
end ALU;

architecture Behavioral of ALU is
signal F         : STD_LOGIC_VECTOR (bus_size downto 0);
signal ALUOutput : STD_LOGIC_VECTOR ((bus_size - 1) downto 0);
begin
  Output <= ALUOutput;
  
  -- ALU process
  process (AluSel, A, B, CarryIn)
  begin
    case AluSel is
	   when op_pass =>    -- Pass A through output
		  F <= '0' & A;
		
		when op_and  =>    -- A and B
		  F <= ('0' & A) and ('0' & B);
		  
		when op_or   =>    -- A or B
		  F <= ('0' & A) or ('0' & B);
		  
		when op_not  =>    -- not A
		  F <= not ('0' & A);		  		
		
		when op_add   =>    -- A + B
		  F <= ('0' & A) + ('0' & B) + CarryIn;
		
		when op_sub   =>    -- A - B
		  F <= ('0' & A) - ('0' & B) - CarryIn;
		
		when op_inc   =>    -- A++
		  F <= ('0' & A) + 1;
		
		when op_dec   =>    -- A--
		  F <= ('0' & A) - 1;
	      
	 end case;
  end process;
  
  -- Shifter behavior
  process (F,ShifterSel)
  begin
    case ShifterSel is
      when op_pass =>           -- Pass F through output
		  ALUOutput <= F((bus_size - 1) downto 0);
		
		when op_shift_left =>    -- Shift left
		  ALUOutput <= F((bus_size - 2) downto 0) & '0';
		
		when op_shift_right =>     -- Shift right
		  ALUOutput <= '0' & F((bus_size - 1) downto 1);
		
		when op_rotate_right =>   -- Rotate right
		  ALUOutput <= F(0) & F((bus_size - 1) downto 1);
	 end case;
  end process;
  
  -- Process to generate flags (Zero, Sign, Carry, Overflow)
  process (F,A,B,ALUOutput,AluSel)
  begin
    -- Carry: set to 1 when there is an unsigned overflow exemple (255 + 1) (result is not in range 0...255)
	 FlagCarry <= F(bus_size);
	 
	 -- Zero: set to 1 when result is zero. For non-zero result this flag is set to 0
	 if ALUOutput = conv_std_logic_vector(0,bus_size) then
	   FlagZero <= '1';
	 else
	   FlagZero <= '0';
	 end if;
	 
	 -- Overflow (http://www.csee.umbc.edu/~squire/cs411_l8.html) and (http://intelliwiki.kylesblog.com/index.php/Overflow_Flag)
	 -- Two complement addition overflow when signs of input are the same but
	 -- sign of output is different
	 -- Overflow only make sense in sign operations.
	 case AluSel is
	   when op_add =>
		  -- When adding we can get two overflow situations.....
		  -- Adding two positive numbers producing a negative result
		  -- OR
		  -- Adding two negative numbers producing a positive result
		  FlagOverflow <= (A(bus_size - 1) and B(bus_size - 1) and (not F(bus_size-1))) or ((not A(bus_size - 1)) and (not B(bus_size - 1)) and F(bus_size-1));
		
		when op_sub =>
		  -- When substracting we can get two overflow situations.....
		  -- Substracting a positive number from a negative number gaving a positive result
		  -- OR
		  -- Substracting a negative number from a positive number gaving a negative result
		  FlagOverflow <= ((not A(bus_size - 1)) and (B(bus_size - 1)) and (not F(bus_size-1)))  or  ((A(bus_size - 1)) and (not B(bus_size - 1)) and F(bus_size-1));
				
		when others =>
		  FlagOverflow <= '0';
	 end case;	 
	 
	 -- Sign: set to 1 when result is negative. When result is positive it is set to 0.
	 FlagSign <= ALUOutput(bus_size-1);
  end process;

end Behavioral;

